package wstm.server;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.transaction.Status;
import javax.xml.namespace.QName;

import wsresource.client.WSResourceManager;
import wsresource.client.WSResourceManagerService;
import wstm.WSTransaction;

@WebService
public class WSTransactionManager {
  private List<WSTransaction> inFlighTransactions;
  public WSTransactionManager() {
    inFlighTransactions = new ArrayList<WSTransaction>();
  }
 
  /**
   * Create a new transaction and active it
   * @return a String object represents the identifier of the transaction
   *         recently created.
   */
  @WebMethod
  public String begin() {
    System.out.println("Begin a new Transaction");
    WSTransaction tx = new WSTransaction();
    // Xid assignment
    Long txId = (System.currentTimeMillis() * 1024);
    tx.myXid = txId.toString();
    tx.status = Status.STATUS_ACTIVE;
    if (inFlighTransactions.add(tx)) {
      System.out.println("WSTM Add tx " + txId + "to in fligh transactions");
    } else {
      System.err.println("WSTM Cannot Add tx " + txId + "to in fligh transactions");
    }
    return txId.toString();
  }

  /**
   * Complete the transaction identified with xid. 
   * When this method successfully complete , the thread is no longer 
   * associated with the transaction.
   * @param xid
   * @return true if success false if failure the transaction 
   *         completed.
   */
  @WebMethod
  public boolean commit(String xid) {
    WSTransaction tx = getTransaction(xid);
    System.out.println("WSTM try commit transaction " + tx.myXid);
    if (tx.commit()) {
      System.out.println("WSTM commited the transaction" + tx.myXid);
      tx.status = Status.STATUS_NO_TRANSACTION;
      //Discard transaction
      inFlighTransactions.remove(tx);
      tx = null;
      return true;
    } else {
      System.err.println("WSTM cannot commit the transaction " + tx.myXid + "");
      tx.setRollbackOnly();
      return false;
    }
  }
  /**
   * Obtain the status of the transaction identified whit the xid .
   * @param xid
   * @return The current transaction status.
   */
  @WebMethod
  public int getStatus(String xid) {
    WSTransaction tx = getTransaction(xid);
    return tx.status;
  }
  /**
   * Get the transaction object that represents the transaction identified
   * with xid.
   * @param xid
   * @return A WSTransaction object representing the transaction
   */
  private WSTransaction getTransaction(String xid) {
    Iterator<WSTransaction> it = inFlighTransactions.iterator();
    while (it.hasNext()) {
      WSTransaction curr = it.next();
      if (curr.myXid.toString().equals(xid)) {
        return curr;
      }
    }
    return null;
  }
  /**
   * Roll back the transaction idenfied with xid and disassociate it.
   * @param xid
   */
  @WebMethod
  public void rollback(String xid) {
    WSTransaction tx = getTransaction(xid);
    System.out.println("WSTM try rollback transaction " + tx.myXid);
    tx.rollback();
    tx.status = Status.STATUS_NO_TRANSACTION;
    //Discard transaction
    inFlighTransactions.remove(tx);
    tx = null;
  }
  /**
   * Modify the transaction associated with the current thread such that
   * the only possible outcome of the transaction is to roll back the
   * transaction.
   * @param xid
   */
  @WebMethod
  public void setRollbackOnly(String xid) {
    WSTransaction tx = getTransaction(xid);
    tx.setRollbackOnly();
  }

  /**
   * Enrol a WSResourceManager, published in wsUrl, in the transaction
   * indentified in xid. 
   * @param xid
   * @param wsUrl
   * @return true if the enroled is succes false if failure. 
   * @throws MalformedURLException
   */
  @WebMethod
  public boolean enrol(String xid, String wsUrl) throws MalformedURLException {
    WSTransaction tx = getTransaction(xid);
    System.out.println("Try Enrol WSResource " + wsUrl + " in transaction " + tx.myXid);
    URL baseUrl = wsresource.client.WSResourceManagerService.class.getResource(".");
    URL url = new URL(baseUrl, wsUrl);
    WSResourceManagerService ws = new WSResourceManagerService( //
        url, new QName("http://server.wsresource/", "WSResourceManagerService"));
    WSResourceManager wsRM = ws.getWSResourceManagerPort();
    //If is alredy enlisted do nothing
    if (tx.isEnlisted(wsRM)) {
      System.out.println(wsRM + " alredy enroled in the transaction " + tx.myXid);
      return true;
    }
    //Enlist in the transaction
    if (tx.enlistResource(wsRM)) {
      System.out.println("WSTM enroled " + wsRM + " in the transaction " + tx.myXid);
      return true;
    } else {
      System.err.println("WSTM Cannot enroled " + wsRM + " in the transaction " + tx.myXid);
      return false;
    }
  }
}
